package log

import (
	"fmt"
	"gitee.com/scottq/go-framework/src/v1/config"
	"runtime"
	"strings"
)

const (
	DebugLog = 1 << iota
	TraceLog
	InfoLog
	WarnLog
	ErrorLog
	PanicLog
	FatalLog
)

var LevelMap = map[int]string{
	TraceLog: "Trace",
	DebugLog: "Debug",
	InfoLog:  "Info",
	WarnLog:  "Warn",
	ErrorLog: "Error",
	FatalLog: "Fatal",
	PanicLog: "Panic",
}
var LevelMapReverse = map[string]int{}

// LogLevelMap 兼容旧配置
var LogLevelMap = LevelMap

func init() {
	for k, v := range LevelMap {
		LevelMapReverse[strings.ToUpper(v)] = k
	}
}

const DefaultLogPath = "./runtime/logs/app.log"

type ILog interface {
	Debug(log string, params ...interface{})
	Info(log string, params ...interface{})
	Warn(log string, params ...interface{})
	Error(log string, params ...interface{})
	Panic(log string, params ...interface{})
	Fatal(log string, params ...interface{})
	Write(p []byte) (n int, err error)

	Ctl(bool) ILog //控制是否打印，如 Ctl(true).Warn(...)
}

// InvokeLog fast use log in struct
type InvokeLog struct {
	logger ILog
}

func (ink *InvokeLog) AddLogger(logger ILog) {
	ink.logger = logger
}

func (ink *InvokeLog) GetLogger() ILog {
	return ink.logger
}

func (ink *InvokeLog) Log(level int8, msg string) {
	if ink.logger == nil {
		return
	}
	switch level {
	case DebugLog:
		ink.logger.Debug(msg)
	case WarnLog:
		ink.logger.Warn(msg)
	case InfoLog:
		ink.logger.Info(msg)
	case ErrorLog:
		ink.logger.Error(msg)
	case FatalLog:
		ink.logger.Fatal(msg)
	case PanicLog:
		ink.logger.Panic(msg)
	}
}

func (ink *InvokeLog) Debug(log string, params ...interface{}) {
	if ink.logger == nil {
		return
	}
	if len(params) <= 0 {
		ink.logger.Debug(log)
		return
	}

	ink.logger.Debug(fmt.Sprintf(log, params...))
	return
}

func (ink *InvokeLog) Info(log string, params ...interface{}) {
	if ink.logger == nil {
		return
	}
	if len(params) <= 0 {
		ink.logger.Info(log)
		return
	}

	ink.logger.Info(fmt.Sprintf(log, params...))
	return
}

func (ink *InvokeLog) Warn(log string, params ...interface{}) {
	if ink.logger == nil {
		return
	}
	if len(params) <= 0 {
		ink.logger.Warn(log)
		return
	}

	ink.logger.Warn(fmt.Sprintf(log, params...))
	return
}
func (ink *InvokeLog) Error(log string, params ...interface{}) {
	if ink.logger == nil {
		return
	}
	if len(params) <= 0 {
		ink.logger.Error(log)
		return
	}

	ink.logger.Error(fmt.Sprintf(log, params...))
	return
}
func (ink *InvokeLog) Panic(log string, params ...interface{}) {

	if ink.logger == nil {
		return
	}
	if len(params) <= 0 {
		ink.logger.Panic(log)
		return
	}

	ink.logger.Panic(fmt.Sprintf(log, params...))
	return
}
func (ink *InvokeLog) Fatal(log string, params ...interface{}) {
	if ink.logger == nil {
		return
	}
	if len(params) <= 0 {
		ink.logger.Fatal(log)
		return
	}

	ink.logger.Fatal(fmt.Sprintf(log, params...))
	return
}
func (ink *InvokeLog) Write(p []byte) (n int, err error) {
	return ink.logger.Write(p)
}
func (ink *InvokeLog) Ctl(t bool) ILog {
	if !t {
		return _nullLog
	}
	return ink
}

type logHelper struct {
}

func (l logHelper) initConfig(c *config.LogConfig) {
	c.LLevel = strings.ToUpper(c.LLevel)
	c.LogLevel = strings.ToUpper(c.LLevel)
}

func (l logHelper) lFilepath(c *config.LogConfig, skip int) string {
	if c == nil {
		return ""
	}

	switch c.LFilepath {
	case 1:
		//短文件名
		_, file, line, _ := runtime.Caller(l.lCallerSkip(c) + skip)
		if index := strings.LastIndex(file, "/"); index >= 0 {
			file = file[index+1:]
		} else if index = strings.LastIndex(file, "\\"); index >= 0 {
			file = file[index+1:]
		}
		return fmt.Sprintf("%s:%d", file, line)
	case 2:
		//完整文件名
		_, file, line, _ := runtime.Caller(l.lCallerSkip(c) + skip)
		return fmt.Sprintf("%s:%d", file, line)
	default:
		return ""
	}
}

func (l logHelper) lCallerSkip(c *config.LogConfig) int {
	if c == nil {
		return 0
	}

	return c.LCallerSkip
}

func (l logHelper) lLogPath(c *config.LogConfig) string {
	if c == nil {
		return ""
	}
	if len(c.LPath) > 0 {
		return c.LPath
	}
	return c.LogPath
}

func (l logHelper) lLogLevel(c *config.LogConfig) string {
	if c == nil {
		return "DEBUG"
	}
	if len(c.LLevel) > 0 {
		return strings.ToUpper(c.LLevel)
	}
	return strings.ToUpper(c.LogLevel)
}

func (l logHelper) lLogLevelInt(c *config.LogConfig) int {
	a, exist := LevelMapReverse[l.lLogLevel(c)]
	if !exist {
		return 0
	}
	return a
}
